package search

import (
	b "code.google.com/p/gochess/board"
	config "code.google.com/p/gochess/config"
	pos "code.google.com/p/gochess/position"
)

// todo: make sure pht is aligned so one entry is in one cache line
var pawnPositions PawnHashTable

// todo: use pawn hash table
// todo: do useful stuff here

const (
	rewardRookOnOpenFile = 20
	rewardBishopPair     = 50
)

// read http://talkchess.com/forum/viewtopic.php?t=40662
func evaluate(p *position) int32 {

	if unsufficientMaterial(p) {
		return 0
	}
	// todo: cut off if material difference is big
	stm := p.Position.SideToMove()
	opponent := pos.Opponent(stm)

	score := p.Material(stm) + p.PositionBonus(stm) -
		(p.Material(opponent) + p.PositionBonus(opponent))

	if config.ExdendedEval {
		if p.PieceList(pos.NewPiece(stm, pos.Bishop)).Max >= 2 {
			score += rewardBishopPair
		}

		var pawnInfo *pawnInfo
		if p.PawnHash() != 0 {
			// there are pawns on the board
			pawnInfo = pawnPositions.get(p.PawnHash())
		}
		_ = pawnInfo

	}
	return score
	/*
		occupiedFiles := 0x00
		if pawnInfo != nil {
			PawnHashHits++
			occupiedFiles = pawnInfo.OpenFiles()
		} else {
			// set info about occupied files
			// var pawns [pos.NUMBER_OF_COLORS][8]b.Square
			// var pawnMax [pos.NUMBER_OF_COLORS]int
			for player := pos.WHITE; player <= pos.BLACK; player++ {
				for piece := pos.KNIGHT; piece <= pos.KING; piece++ {
					pieces := p.PieceList(pos.NewPiece(piece, player))
					for i := 0; i < pieces.Max; i++ {
						file, err := b.GetFile(pieces.Pieces[i])
						// assert
						if config.Debug && err != nil {
							panic(err)
						}
						occupiedFiles |= 1 << uint(file-1)
					}
				}
			}
			pawnPositions.Save(p.PawnHash(), occupiedFiles)
		}

		var reward [pos.NUMBER_OF_COLORS]int
		for player := pos.WHITE; player <= pos.BLACK; player++ {
			pieces := p.PieceList(pos.NewPiece(pos.ROOK, player))
			for i := 0; i < pieces.Max; i++ {
				file, err := b.GetFile(pieces.Pieces[i])
				// assert
				if config.Debug && err != nil {
					panic(err)
				}
				if (1 << uint(file-1) & occupiedFiles) == 0 {
					// rook on empty file
					reward[player] += rewardRookOnOpenFile
				}
			}

				//if bishops > 1 {
				//	// bishop pair
				//	reward[color] += rewardBishopPair
				//}
		}
	*/
}

var pieceValue [pos.BiggestPiece + 1]int32

func init() {

	// we store piece type values into the pieces array, as
	// a piece type is a piece with no color
	for _, color := range []pos.Color{pos.NoColor, pos.White, pos.Black} {
		pieceValue[pos.NewPiece(color, pos.King)] = kingValue
		pieceValue[pos.NewPiece(color, pos.Queen)] = queenValue
		pieceValue[pos.NewPiece(color, pos.Rook)] = rookValue
		pieceValue[pos.NewPiece(color, pos.Bishop)] = bishopValue
		pieceValue[pos.NewPiece(color, pos.Knight)] = knightValue
		pieceValue[pos.NewPiece(color, pos.Pawn)] = pawnValue
	}
}

// Value returns the value of the piece in centipawns.
func value(piece pos.Piece) int32 {
	return pieceValue[piece]
}

// Values of the pieces in centipawns.
// The values used are proposed by Tomasz Michniewski, see
// http://chessprogramming.wikispaces.com/Simplified+evaluation+function
const (
	pawnValue   int32 = 100
	knightValue int32 = 320
	bishopValue int32 = 330
	rookValue   int32 = 500
	queenValue  int32 = 900
	// was 25000. we never us this value, so we set it to 0. if things break
	// then we know there is a serious bug, so its actually a good thing to do.
	kingValue int32 = 0
)

// see http://www.e4ec.org/immr.html
func unsufficientMaterial(p *position) bool {
	// we should keep track of pieces, so we can do:
	// if pieces > 4...
	if p.PieceList(pos.WhitePawn).Max != 0 || p.PieceList(pos.BlackPawn).Max != 0 ||
		p.PieceList(pos.WhiteRook).Max != 0 || p.PieceList(pos.BlackRook).Max != 0 ||
		p.PieceList(pos.WhiteQueen).Max != 0 || p.PieceList(pos.BlackQueen).Max != 0 ||
		p.PieceList(pos.WhiteKnight).Max+p.PieceList(pos.BlackKnight).Max > 1 ||
		p.PieceList(pos.WhiteKnight).Max+p.PieceList(pos.BlackKnight).Max == 1 &&
			p.PieceList(pos.WhiteBishop).Max+p.PieceList(pos.BlackBishop).Max > 0 {
		return false
	}
	// we are still here, so we know: there is
	// - 1...n bishops only OR
	// - one knight only
	if p.PieceList(pos.WhiteKnight).Max+p.PieceList(pos.BlackKnight).Max == 1 {
		return true
	}
	// bishops only!
	wb := p.PieceList(pos.WhiteBishop)
	bb := p.PieceList(pos.BlackBishop)
	// init with border piece
	var sq1, sq2 b.Square
	if wb.Max > 0 {
		sq1 = wb.Pieces[0]
		for i := int8(1); i < wb.Max; i++ {
			if !b.SameColor(sq1, wb.Pieces[i]) {
				return false
			}
		}
		// all bishops on the same square color
		// if the opponent has no bishops we know its draw
		if bb.Max == 0 {
			return true
		}
	}
	if bb.Max > 0 {
		sq2 = bb.Pieces[0]
		for i := int8(1); i < bb.Max; i++ {
			if !b.SameColor(sq2, bb.Pieces[i]) {
				return false
			}
		}
		// all bishops on the same square color
		// if the opponent has no bishops we know its draw
		if wb.Max == 0 {
			return true
		}
	}
	// now we know that each side has bishops, which are
	// on the same square for black and white
	// we have draw if all bishops on both sides
	// are on the same square color:
	return b.SameColor(sq1, sq2)
}

func kingSafety(p *position) int32 {
	return 0
}
